﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace Ukagaka.Named.Ghost.SakuraScript
{
	/// <summary>
	/// 文字列の移動参照を表現します
	/// </summary>
	public class StringSeeker : IEnumerable<char>
	{
		/// <summary>
		/// 文字列を取得します
		/// </summary>
		public string Source
		{
			get;
			private set;
		}

		/// <summary>
		/// 文字列の長さを取得します
		/// </summary>
		public int Length
		{
			get
			{
				return Source.Length;
			}
		}

		/// <summary>
		/// 参照位置を取得または設定します
		/// </summary>
		private int _Position;

		/// <summary>
		/// 参照位置を取得または設定します
		/// </summary>
		public int Position
		{
			get
			{
				return _Position;
			}
			set
			{
				if ( CheckSeek( value ) )
				{
					_Position = value;
				}
				else
				{
					throw new IndexOutOfRangeException( "文字列の範囲外に移動しようとしました" );
				}
			}
		}

		/// <summary>
		/// 参照位置が文字列の終端かどうかを取得します
		/// </summary>
		public bool EndOfSource
		{
			get
			{
				return Position >= Length;
			}
		}

		/// <summary>
		/// 文字列の移動参照を表現するインスタンスを作成します
		/// </summary>
		/// <param name="source">文字列</param>
		public StringSeeker( string source )
		{
			Source = source;
		}

		/// <summary>
		/// 次の文字に移動します
		/// </summary>
		/// <returns>次の文字が存在する場合はtrue、文字列の終端の場合はfalse。</returns>
		public bool MoveNext( )
		{
			if ( CheckSeek( Position + 1 ) )
			{
				Position = Position + 1;

				return !EndOfSource;
			}
			else
			{
				return false;
			}
		}

		/// <summary>
		/// 参照位置が文字列に対して移動可能かどうかを取得します
		/// </summary>
		/// <param name="position">参照位置</param>
		/// <returns>参照位置が文字列に対して移動可能な場合はtrue、でなければfalse。</returns>
		public bool CheckSeek( int position )
		{
			return Length >= position && position >= 0;
		}

		/// <summary>
		/// 参照位置の文字を取得します
		/// </summary>
		/// <param name="position">参照位置</param>
		/// <returns>参照位置の文字。文字列の終端の場合は-1。</returns>
		public int GetChar( int position )
		{
			return CheckSeek( position + 1 ) ? Source[ position ] : -1;
		}

		/// <summary>
		/// 参照位置のひとつ前の文字を取得します
		/// </summary>
		/// <returns>参照位置のひとつ前の文字。範囲外の場合は-1。</returns>
		public int GetBackChar( )
		{
			return GetChar( Position - 1 );
		}

		/// <summary>
		/// 参照位置の文字を取得します
		/// </summary>
		/// <returns>参照位置の文字。範囲外の場合は-1。</returns>
		public int GetSeekChar( )
		{
			return GetChar( Position );
		}

		/// <summary>
		/// 参照位置のひとつ後の文字を取得します
		/// </summary>
		/// <returns>参照位置のひとつ後の文字。範囲外の場合は-1。</returns>
		public int GetNextChar( )
		{
			return GetChar( Position + 1 );
		}

		#region IEnumerable<char> メンバー

		/// <summary>
		/// 文字列に含まれる文字を反復処理する列挙子を取得します
		/// </summary>
		/// <returns>文字列に含まれる文字を反復処理する列挙子</returns>
		public IEnumerator<char> GetEnumerator( )
		{
			return Source.GetEnumerator( );
		}

		#endregion

		#region IEnumerable メンバー

		/// <summary>
		/// 文字列に含まれる文字を反復処理する列挙子を取得します
		/// </summary>
		/// <returns>文字列に含まれる文字を反復処理する列挙子</returns>
		IEnumerator IEnumerable.GetEnumerator( )
		{
			return GetEnumerator( );
		}

		#endregion
	}
}
